﻿using System.Collections;
using System.Collections.Generic;
using System.Web.Script.Serialization;
using Utilities;

namespace JiraRest
{
    /// <summary>
    /// See http://docs.atlassian.com/jira/REST
    /// for documentation of the REST API.
    /// See http://confluence.atlassian.com/display/JIRA/Advanced+Searching
    /// for documentation of JQL searches.
    /// </summary>
    public sealed class JiraConnection
    {
        private static readonly JavaScriptSerializer s_Json = new JavaScriptSerializer();

        //private readonly JiraSoapServiceClient m_Client;
        private readonly string m_UserName;
        private readonly string m_JiraServer;
        private readonly WebClient m_WebClient;

        private readonly IDictionary<string, string> m_IssueTypeNameToId = new Dictionary<string, string>();
        private readonly IDictionary<string, string> m_ResolutionNameToId;
        private readonly IDictionary<string, string> m_CustomFieldNameToId = new Dictionary<string, string>();
        private readonly IDictionary<string, string> m_ActionNameToId = new Dictionary<string, string>();
        private readonly IDictionary<int, string> m_StatusIdToName = new Dictionary<int, string>();
        private readonly HashSet<string> m_SettableFields = new HashSet<string>(); 


        public JiraConnection(string userName, string password, string jiraServer, WebClient webClient)
        {
            m_UserName = userName;
            m_JiraServer = jiraServer;
            m_WebClient = webClient;

            // Make sure we're logged in (the server remembers, we don't need a token)
            Auth("session", s_Json.Serialize(new Dictionary<string, string>{{"username", userName}, {"password", password}}));

            // Read the possible issue types. This api call may be huge
            string createmetaJson = Api("issue/createmeta?expand=projects.issuetypes.fields");
            dynamic createmeta = DynamicJsonConverter.Deserialize(createmetaJson);

            // This lists the issue types within the projects which allow them, but we just need a map to issue type ID, so we can union them
            foreach (dynamic eachProject in createmeta.projects)
            {
                foreach (dynamic eachIssueType in eachProject.issuetypes)
                {
                    m_IssueTypeNameToId[eachIssueType.name] = eachIssueType.id;

                    foreach (KeyValuePair<string, object> eachField in eachIssueType.fields.Dictionary)
                    {
                        Dictionary<string, object> fieldInfo = (Dictionary<string, object>) eachField.Value;
                        ArrayList operations = (ArrayList) fieldInfo["operations"];
                        if (operations.Contains("set"))
                        {
                            m_SettableFields.Add(eachField.Key);
                        }
                    }
                }
            }

            dynamic statuses = DynamicJsonConverter.Deserialize(Api("status"));

            foreach (dynamic eachStatus in statuses)
            {
                m_StatusIdToName[int.Parse(eachStatus.id)] = eachStatus.name;
            }

            //RemoteResolution[] resolutions = m_Client.getResolutions(m_Token);
            //m_ResolutionNameToId = resolutions.ToDictionary(x => x.name, x => x.id, StringComparer.InvariantCultureIgnoreCase);
        }

        public string Get(string query)
        {
            return m_WebClient.Get(m_JiraServer + @"/rest/" + query);
        }

        public string Api(string query)
        {
            return Get("api/2/" + query);
        }

        public string Auth(string query)
        {
            return Get("auth/1/" + query);
        }

        public string Post(string query, string content)
        {
            return m_WebClient.Post(m_JiraServer + @"/rest/" + query, content);
        }

        public string Api(string query, string content)
        {
            return Post("api/2/" + query, content);
        }

        public string Auth(string query, string content)
        {
            return Post("auth/1/" + query, content);
        }

        public string Put(string query, string content)
        {
            return m_WebClient.Put(m_JiraServer + @"/rest/" + query, content);
        }

        public string PutApi(string query, string content)
        {
            return Put("api/2/" + query, content);
        }

        public string JiraServer
        {
            get { return m_JiraServer; }
        }
        
        internal string GetIssueTypeId(string name)
        {
            return m_IssueTypeNameToId[name];
        }

        internal bool FieldIsSettable(string fieldName)
        {
            return m_SettableFields.Contains(fieldName);
        }

        //internal string GetCustomFieldId(RemoteIssue remoteIssue, string customFieldName)
        //{
        //    CheckDisposed();

        //    string customFieldId;
        //    if (!m_CustomFieldNameToId.TryGetValue(customFieldName, out customFieldId))
        //    {
        //        RemoteField[] fields = m_Client.getFieldsForEdit(m_Token, remoteIssue.key);
        //        if (fields != null)
        //        {
        //            foreach (RemoteField field in fields)
        //            {
        //                // merge over the top if it's already there
        //                m_CustomFieldNameToId[field.name] = field.id;
        //            }
        //        }

        //        if (!m_CustomFieldNameToId.TryGetValue(customFieldName, out customFieldId))
        //        {
        //            throw new Exception(String.Format("Could not find field {0} for issue {1}. Check that the field exists and that the user {2} has permission to access it", customFieldName, remoteIssue.key, m_UserName));
        //        }
        //    }

        //    return customFieldId;
        //}

        //internal string GetActionId(RemoteIssue remoteIssue, string actionName)
        //{
        //    CheckDisposed();

        //    string actionId;
        //    if (!m_ActionNameToId.TryGetValue(actionName, out actionId))
        //    {
        //        RemoteNamedObject[] namedObjects = m_Client.getAvailableActions(m_Token, remoteIssue.key);
        //        if (namedObjects != null)
        //        {
        //            foreach (RemoteNamedObject namedObject in namedObjects)
        //            {
        //                // merge over the top if it's already there
        //                m_ActionNameToId[namedObject.name] = namedObject.id;
        //            }
        //        }

        //        if (!m_ActionNameToId.TryGetValue(actionName, out actionId))
        //        {
        //            throw new Exception(String.Format("Could not find action {0} for issue {1}. Check that the action exists and that the user {2} has permission to perform it", actionName, remoteIssue.key, m_UserName));
        //        }
        //    }

        //    return actionId;
        //}

        internal string GetResolutionId(string name)
        {
            return m_ResolutionNameToId[name];
        }

        internal string GetStatusName(int id)
        {
            return m_StatusIdToName[id];
        }
    }
}
